/*
*/

#include "FBXFile.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <stack>
#ifndef ASSERT
#include <assert.h>
#define ASSERT
#endif


static const char* g_vBVHKeyWords[]={
    "HIERARCHY",
    "ROOT",
    "OFFSET",
    "CHANNELS",
    "JOINT",
    "End Site",
    "MOTION",
    "Frames:",
    "Frame Time:",
    "Xposition",
    "Yposition",
    "Zposition",
    "Xrotation",
    "Yrotation",
    "Zrotation"
};

static const char* g_vBonesNames[]={
    "reference",
    "Hips",
    "RightUpLeg",
    "RightLeg",
    "RightFoot",
    "LeftUpLeg",
    "LeftLeg",
    "LeftFoot",
    "Spine",
    "Spine1",
    "Spine2",
    "Spine3",
    "Neck",
    "Head",
    "RightShoulder",
    "RightArm",
    "RightForeArm",
    "RightHand",
    "LeftShoulder",
    "LeftArm",
    "LeftForeArm",
    "LeftHand"
};

#define BVH_KEYWORD_HIERARCHY   g_vBVHKeyWords[0]
#define BVH_KEYWORD_ROOT        g_vBVHKeyWords[1]
#define BVH_KEYWORD_OFFSET      g_vBVHKeyWords[2]
#define BVH_KEYWORD_CHANNELS    g_vBVHKeyWords[3]
#define BVH_KEYWORD_JOINT       g_vBVHKeyWords[4]
#define BVH_KEYWORD_END_SITE    g_vBVHKeyWords[5]
#define BVH_KEYWORD_MOTION      g_vBVHKeyWords[6]
#define BVH_KEYWORD_FRAMES      g_vBVHKeyWords[7]
#define BVH_KEYWORD_FRAME_TIME  g_vBVHKeyWords[8]
#define BVH_KEYWORD_XPOSITION   g_vBVHKeyWords[9]
#define BVH_KEYWORD_YPOSITION   g_vBVHKeyWords[10]
#define BVH_KEYWORD_ZPOSITION   g_vBVHKeyWords[11]
#define BVH_KEYWORD_XROTATION   g_vBVHKeyWords[12]
#define BVH_KEYWORD_YROTATION   g_vBVHKeyWords[13]
#define BVH_KEYWORD_ZROTATION   g_vBVHKeyWords[14]

#define BVH_LEFT_BRACE  "{"
#define BVH_RIGHT_BRACE "}"

static
    bool StringEqual    (   NPCBYTE     pbStart, 
                            NPCBYTE     pbEnd,
                            NLPCSTR     szkeyWord   )
{
    ASSERT(pbStart<pbEnd);
    while ( 0!=*szkeyWord
        &&  pbStart<pbEnd
        &&  *pbStart==*szkeyWord){
            pbStart++;
            szkeyWord++;
    }
    return (0==*szkeyWord);
}

static
    NPCBYTE memory_search   (   NPCBYTE         pbStart, 
                                NPCBYTE         pbEnd,
                                NLPCSTR         find    )
{
    ASSERT(NULL != pbStart);
    ASSERT(pbEnd>pbStart);
    ASSERT(NULL != find);

    char c, sc;
    if ((c = *find++) != '\0') {
        do {
            do {
                sc = *pbStart++;
                if (pbStart>=pbEnd){
                    return pbEnd;
                }
            } while (sc != c);
        } while (!StringEqual(pbStart, pbEnd, find));
        pbStart--;
    }
    return pbStart;
}



CBVHParser::ChannelType ChannelValue(   NPCBYTE     pbStart, 
                                        NPCBYTE     pbEnd    )
{
    ASSERT(pbStart<pbEnd);
    static CBVHParser::ChannelType vChannels[]={
        CBVHParser::Xposition,
        CBVHParser::Yposition,
        CBVHParser::Zposition,
        CBVHParser::Xrotation,
        CBVHParser::Yrotation,
        CBVHParser::Zrotation
    };
    static const char* vChannelsNames[]={
        BVH_KEYWORD_XPOSITION,
        BVH_KEYWORD_YPOSITION,
        BVH_KEYWORD_ZPOSITION,
        BVH_KEYWORD_XROTATION,
        BVH_KEYWORD_YROTATION,
        BVH_KEYWORD_ZROTATION
    };
    ASSERT(_countof(vChannels)==_countof(vChannelsNames));
    for (int i=0;i<_countof(vChannelsNames);i++){
        if(!StringEqual(pbStart,pbEnd, vChannelsNames[i])){
            continue;
        }
        else{
            return vChannels[i];
        }
    }
    ASSERT(false);
    return CBVHParser::Invalid;
}

NPCBYTE SkipSpaces  (   NPCBYTE     pbStart, 
                        NPCBYTE     pbEnd    )
{
    while ( pbStart<pbEnd
        &&  ::isspace(*pbStart))
        pbStart++;
    return (pbStart<pbEnd?pbStart:pbEnd);
}

NPCBYTE SkipWord    (   NPCBYTE   pbStart, 
                        NPCBYTE   pbEnd, 
                        NLPCSTR   szkeyWord   )
{
    while ( 0!=*szkeyWord
        &&  pbStart<pbEnd
        &&  *pbStart==*szkeyWord){
        pbStart++;
        szkeyWord++;
    }
    if(     0==*szkeyWord
        &&  pbStart<pbEnd){
        if(isspace(*pbStart)){
            return ::SkipSpaces(pbStart,pbEnd);
        }
        else if(ispunct(*(--szkeyWord))){
            return pbStart;
        }
    }
    return pbEnd;
}

static
    double  atof_s  (   const char  *   s,
                        unsigned int    ssize)
{
    ASSERT(ssize>0);
    char*ss=(char*)::malloc(ssize+1);
    memcpy(ss,s,ssize);
    ss[ssize]=0;
    double dr = ::atof(ss);
    free(ss);
    return dr;
}

const unsigned char *   SkipIntger  (   const unsigned char *   pbStart, 
                                     const unsigned char *   pbEnd,
                                     int                 &   nValue  )
{
    int  a = 0;
    int e = 0;
    int c;
    while (     pbStart<pbEnd
        &&  !isspace(c=*pbStart++)
        &&  isdigit(c)  ) {
            a = a*10 + (c - '0');
    }
    nValue=a;
    return pbStart;
}

const unsigned char *   SkipWord    (   const unsigned char *   pbStart, 
                                     const unsigned char *   pbEnd   )
{
    while ( !isspace(*pbStart)
        &&  pbStart<pbEnd){
            pbStart++;            
    }
    return (pbStart<pbEnd?pbStart:NULL);        
}

const unsigned char *   SkipFloat   (   const unsigned char *   pbStart, 
                                     const unsigned char *   pbEnd, 
                                     double              &   fValue  )
{
    const unsigned char * pbWordEnd = ::SkipWord(pbStart, pbEnd);
    unsigned int size = pbWordEnd-pbStart;
    char * szFloat = (char*)::malloc(size+1);
    memcpy(szFloat, pbStart,size);
    szFloat[size]=0;
    fValue=::atof(szFloat);
    free(szFloat);
    return pbWordEnd;
}

static
    bool    IsLineBreak(int c)
{
    return c=='\n';
}

#define SKIP_SPACES_CONTINUE()   \
    pbStart=::SkipSpaces(pbNexWord,pbEnd);\
    continue

#define NEW_JOINT() \
    const unsigned char * pbWordEnd = ::SkipWord(pbNexWord, pbEnd);\
    pNewJoint = new CBVHJoint(pbNexWord, pbWordEnd);\
    pNewJoint->m_szJointIndex=m_vJoints.size();\
    pNewJoint->m_bHasSite=false;\
    m_vJoints.push_back(pNewJoint);\
    pbStart = ::SkipSpaces(pbWordEnd, pbEnd)

#define ELSE_SKIP_WORD(word)    \
    else{\
        pbNexWord = ::SkipWord(pbStart, pbEnd, word);\
    }


CBVHParser::CBVHJoint::CBVHJoint    (   NPCBYTE pbStart, 
                                        NPCBYTE pbEnd   )
                                     :   m_pParentJoint (   NULL    )
                                     ,   m_nChannels    (   0       )
                                     ,  m_szJointIndex  (   0       )
{
    unsigned int size = pbEnd-pbStart;
    m_szTag = (unsigned char*)::malloc(size+1);
    memcpy(m_szTag, pbStart, size);
    m_szTag[size]=0;
}

CBVHParser::CBVHJoint::~CBVHJoint()
{
    if(NULL != m_szTag){
        free(m_szTag);
    }
}

void    CBVHParser::CBVHJoint::AddChild        (   CBVHJoint   *   pChildJoint)
{
    m_vChildren.push_back(pChildJoint);
}

NPCBYTE CBVHParser::CBVHJoint::ParseOffset  (   NPCBYTE                 pbStart, 
                                                NPCBYTE                 pbEnd,
                                                bool                    bEndSite   )
{
    ASSERT(pbStart<pbEnd);

    double (*pvOffset)[3] = NULL;
    if(bEndSite){
        ASSERT(m_bHasSite);
        pvOffset=&m_vSiteOffset;
    }
    else{
        pvOffset=&m_vOffset;
    }
    double dfValue=0.0f;
    NPCBYTE pbNexWord=::SkipFloat(pbStart, pbEnd, dfValue);
    (*pvOffset)[0]=dfValue;
    if(pbNexWord<pbEnd){
        pbNexWord=::SkipSpaces(pbNexWord,pbEnd);
    }
#ifdef _DEBUG
    else{
        ASSERT(false);
    }
#endif
    if(pbNexWord<pbEnd){
        pbNexWord=SkipFloat(pbNexWord, pbEnd, dfValue);
        (*pvOffset)[1]=dfValue;
    }
#ifdef _DEBUG
    else{
        ASSERT(false);
    }
#endif
    if(pbNexWord<pbEnd){
        pbNexWord=::SkipSpaces(pbNexWord,pbEnd);
    }
#ifdef _DEBUG
    else{
        ASSERT(false);
    }
#endif
    if(pbNexWord<pbEnd){
        pbNexWord=SkipFloat(pbNexWord, pbEnd, dfValue);
        (*pvOffset)[2]=dfValue;
    }
#ifdef _DEBUG
    else{
        ASSERT(false);
    }
#endif
    if(pbNexWord<pbEnd){
        return SkipSpaces(pbNexWord,pbEnd);
    }
    else{
        return pbEnd;
    }
}

NPCBYTE   CBVHParser::CBVHJoint::ParseChannels   (  NPCBYTE   pbStart, 
                                                    NPCBYTE   pbEnd )
{
    ASSERT(pbStart<pbEnd);

    int nChannels=0;
    NPCBYTE pbNexWord=::SkipIntger(pbStart,pbEnd, nChannels),
            pbWordEnd=NULL;

    m_nChannels=nChannels;
    while (     nChannels--
        &&  pbNexWord<pbEnd ){
            pbWordEnd=::SkipWord(pbNexWord, pbEnd);
            if(pbWordEnd!=pbEnd){
                m_vChannels.push_back(::ChannelValue(pbNexWord,pbWordEnd));
                pbNexWord=::SkipSpaces(pbWordEnd, pbEnd);
            }
            else{
                ASSERT(false);
                break;
            }
    }
    if(m_vChannels.size()==m_nChannels){
        return pbNexWord;
    }
    else{
        ASSERT(false);
        return pbEnd;
    }
}

CBVHParser::CChannel::CChannel  (   CBVHParser::ChannelType     type, 
                                    CBVHParser::CBVHJoint    *  pJoint  )
    :   m_eChannel  (   type    )
    ,   m_pJoint    (   pJoint  )
{}

CBVHParser::CChannel::~CChannel   ()
{
    //
}


CBVHParser::CBVHParser(void)
    :   m_nFrames           (   0   )
    ,   m_dfFrameInterval   (   0.0 )
    ,   m_pbCacheBuffer     (   NULL    )
    ,   m_cbCacheBuffer     (   0       )
    ,   m_cbCacheBufferPos  (   0       )
    ,   m_pDelegate         (   NULL    )
{
    m_szLineBreak=::_strdup("||");
    m_szWordBreak=::_strdup(" ");
}

CBVHParser::~CBVHParser(void)
{
}

NPCBYTE CBVHParser::ParseHierarchy    (     NPCBYTE     pbBuffer, 
                                            NSIZE       cbBuffer    )
{
    NPCBYTE     pbEndBuffer     = pbBuffer+cbBuffer,
                pbStartBuffer   = ::SkipSpaces(pbBuffer, pbEndBuffer);
    if(pbStartBuffer<pbEndBuffer){
        pbStartBuffer = ::SkipWord(pbStartBuffer, pbEndBuffer, BVH_KEYWORD_HIERARCHY);
        if(pbStartBuffer<pbEndBuffer){
            //
            return ParseJoints(pbStartBuffer,pbEndBuffer);
        }
    }
    //
    return pbStartBuffer;
}

NPCBYTE CBVHParser::ParseJoints (   NPCBYTE   pbStart, 
                                    NPCBYTE   pbEnd       )
{
    stack<CBVHJoint*>   jointStack;
    CBVHJoint   * pJoint        = NULL,
                * pNewJoint     = NULL;
    bool isEndSite=false;
    while (pbStart<pbEnd){
        const unsigned char *pbNexWord = ::SkipWord(pbStart, pbEnd, BVH_KEYWORD_ROOT);
        if(pbEnd != pbNexWord){   //  Root
            NEW_JOINT();
            continue;
        }
        ELSE_SKIP_WORD(BVH_KEYWORD_JOINT);
        if(pbEnd != pbNexWord){   //  Joint
            NEW_JOINT();
            ASSERT(NULL != pJoint);
            pNewJoint->m_pParentJoint=pJoint;
            pJoint->AddChild(pNewJoint);
            continue;
        }
        ELSE_SKIP_WORD(BVH_KEYWORD_OFFSET);
        if(pbEnd != pbNexWord){   //  Offset
            pbStart = pJoint->ParseOffset(pbNexWord,pbEnd, isEndSite);
            continue;
        }
        ELSE_SKIP_WORD(BVH_KEYWORD_CHANNELS);
        if(pbEnd != pbNexWord){   //  Channels
            pbStart=pJoint->ParseChannels(pbNexWord, pbEnd);
            AddChannels(pJoint);
            continue;;
        }
        ELSE_SKIP_WORD(BVH_KEYWORD_END_SITE);
        if(pbEnd != pbNexWord){   // End Site
            isEndSite=true;
            pJoint->m_bHasSite=true;
            pNewJoint=pJoint;
            SKIP_SPACES_CONTINUE();
        }
        ELSE_SKIP_WORD(BVH_LEFT_BRACE);
        if(pbEnd != pbNexWord){   // {
            jointStack.push(pJoint);
            pJoint = pNewJoint;
            SKIP_SPACES_CONTINUE();
        }
        ELSE_SKIP_WORD(BVH_RIGHT_BRACE);
        if(pbEnd != pbNexWord){   // }
            pJoint=jointStack.top();
            jointStack.pop();
            if(isEndSite){
                isEndSite = false;
            }
            SKIP_SPACES_CONTINUE();
        }
        else{
            break;
        }
    }
    return pbStart;
}

unsigned int    CBVHParser::ParseFrames (   NPCBYTE   pbStart, 
                                            NPCBYTE   pbEnd       )
{
    m_szLineBreak=::_strdup("\n");
    m_szWordBreak=::_strdup(" ");

    NPCBYTE pbFrameEnd = DirectParse(pbStart, pbEnd);
    return m_vBVHFrame.size();
}

bool    CBVHParser::ParseFile  (   const char* szFile  )
{
    FILE* pf = ::fopen(szFile,"rb");
    ::fseek(pf, 0, SEEK_END);
    long size = ::ftell(pf);
    ::fseek(pf, 0, SEEK_SET);
    unsigned int cbBuffer = size;
    unsigned char* pbStart = (unsigned char*)::malloc(size);
    ::memset(pbStart, 0, size);
    ::fread(pbStart, 1, cbBuffer, pf);
    ::fclose(pf);
    NPCBYTE pbEnd=pbStart+cbBuffer;
    NPCBYTE pbMotionData = ParseHierarchy(pbStart, cbBuffer);

    pbMotionData = ::SkipWord(pbMotionData, pbEnd, BVH_KEYWORD_MOTION);
    if(pbMotionData<pbEnd){
        pbMotionData=::SkipWord(pbMotionData, pbEnd, BVH_KEYWORD_FRAMES);
    }
    if(pbMotionData<pbEnd){
        int nFrame=0;
        pbMotionData=::SkipIntger(pbMotionData,pbEnd, nFrame);
        m_nFrames=nFrame;
    }
    if(pbMotionData<pbEnd){
        pbMotionData=::SkipSpaces(pbMotionData,pbEnd);
    }
    if(pbMotionData<pbEnd){
        pbMotionData=::SkipWord(pbMotionData, pbEnd, BVH_KEYWORD_FRAME_TIME);
    }
    if(pbMotionData<pbEnd){
        pbMotionData=::SkipSpaces(pbMotionData,pbEnd);
    }
    if(pbMotionData<pbEnd){
        double dfFrameTime=0.0f;
        pbMotionData=::SkipFloat(pbMotionData,pbEnd,dfFrameTime);
        m_dfFrameInterval=dfFrameTime;
    }
    if(pbMotionData<pbEnd){
        pbMotionData=::SkipSpaces(pbMotionData,pbEnd);
    }
    if(pbMotionData<pbEnd){
        NSIZE nFrame = ParseFrames(pbMotionData,pbEnd);
        if(nFrame!=m_nFrames){
            ASSERT(false);
            m_nFrames=nFrame;
        }
    }
    ::free(pbStart);
    return (pbMotionData<pbEnd);
}

void    CBVHParser::SetLineBreak    (  NLPCSTR  pbLineBreak )
{
    ASSERT(false);
}

void    CBVHParser::SetWordBreak    (  NLPCSTR  pbWorkBreak )
{
    ASSERT(false);
}

bool    CBVHParser::CacheIsEmpty    (   void    )   const
{
    if(     NULL==m_pbCacheBuffer
        ||  0==m_cbCacheBufferPos   ){
            return true;
    }
    ASSERT(m_cbCacheBuffer>0);
    ASSERT(m_cbCacheBuffer>=m_cbCacheBufferPos);
    return false;
}

void    CBVHParser::ClearCache      (   void    )
{
    if(NULL==m_pbCacheBuffer){
        return;
    }
    ASSERT(m_cbCacheBuffer>0);
    ASSERT(m_cbCacheBuffer>=m_cbCacheBufferPos);
    if(m_cbCacheBufferPos>0){
        m_cbCacheBufferPos=0;
    }
}

void    CBVHParser::CachedParseInteral  (   NPCBYTE  pbBuffer,
                                            unsigned int    cbBuffer    )
{
    NPCBYTE pbLastBuf = DirectParse(pbBuffer, cbBuffer);
    if(pbLastBuf<(pbBuffer+cbBuffer)){
        CacheBlock(pbLastBuf, pbBuffer+cbBuffer-pbLastBuf);
    }
}

bool    CBVHParser::CachedParse     (  NPCBYTE   pbBuffer, 
                                      unsigned int    cbBuffer    )
{
    vector<BVH_FLOAT>  vFrame;
    if(CacheIsEmpty()){
        CachedParseInteral(pbBuffer,cbBuffer);
    }
    else{
        NPCBYTE pbEnd = ::memory_search(pbBuffer,pbBuffer+cbBuffer ,m_szLineBreak);
        if(NULL==pbEnd){
            CacheBlock(pbBuffer, cbBuffer);
        }
        else{            
            CacheBlock(pbBuffer, pbEnd-pbBuffer);
            ParseCache();
            ClearCache();
            pbEnd+=strlen(m_szLineBreak);
            CachedParseInteral(pbEnd,pbBuffer+cbBuffer-pbEnd);
        }
    }
    return true;
}

void    CBVHParser::CacheBlock      (  NPCBYTE  pbBuffer,
                                      unsigned int    cbBuffer    )
{
    if(0==m_pbCacheBuffer){
        m_pbCacheBuffer = (NPBYTE)::malloc(cbBuffer);
        m_cbCacheBuffer = cbBuffer;
    }
    else if(m_cbCacheBufferPos+cbBuffer>m_cbCacheBuffer){
        m_pbCacheBuffer = (NPBYTE)::realloc(m_pbCacheBuffer, m_cbCacheBufferPos+cbBuffer);
        m_cbCacheBuffer=m_cbCacheBufferPos+cbBuffer;
    }
    if(0!=m_pbCacheBuffer){
        memcpy(m_pbCacheBuffer+m_cbCacheBufferPos, pbBuffer, cbBuffer);
        m_cbCacheBufferPos+=cbBuffer;
    }
}

void    CBVHParser::ParseCache      (   void    )   const
{
    DirectParse(m_pbCacheBuffer, m_cbCacheBufferPos);
}

NPCBYTE  CBVHParser::DirectParse (       NPCBYTE     pbBuffer,
                                        unsigned int    cbBuffer    )   const
{
    NPCBYTE pbStart     = pbBuffer,
            pbEnd       = NULL,
            pbBufferEnd = pbBuffer+cbBuffer;
    do 
    {
        pbEnd = ::memory_search(pbStart, pbBufferEnd,m_szLineBreak);
        
        if(     NULL != pbEnd
            &&  pbEnd<pbBufferEnd   ){
                PBVH_FRAME pFrame = ParseFrame(pbStart, pbEnd);
            if(     NULL != pFrame
                &&  NULL != m_pDelegate){
                m_pDelegate->OnFrame(pFrame);
            }
            pbStart=pbEnd+::strlen(m_szLineBreak);
        }
    } while (   NULL != pbEnd
            &&  pbStart<pbBufferEnd   );
    return pbStart;
}


NPCBYTE     CBVHParser::DirectParse         (   NPCBYTE         pbStart,
                                                NPCBYTE         pbEnd       )
{
    NPCBYTE pbFrameStart    = pbStart,
            pbFrameEnd      = NULL;
    do 
    {
        pbFrameEnd = ::memory_search(pbFrameStart, pbEnd,m_szLineBreak);
        PBVH_FRAME pFrame = ParseFrame(pbFrameStart, pbFrameEnd);
        if(     NULL != pFrame  ){
            m_vBVHFrame.push_back(pFrame);
            if(NULL != m_pDelegate){
                m_pDelegate->OnFrame(pFrame);
            }
        }
        pbFrameStart=pbFrameEnd+::strlen(m_szLineBreak);
    } while (   pbFrameStart<pbEnd   );
    return pbFrameStart;
}

static
    CBVHParser::PBVH_FRAME  NewBVHFrame (   NSIZE szSegments    )
{
    CBVHParser::PBVH_FRAME pFrame = (CBVHParser::PBVH_FRAME)::malloc(sizeof(CBVHParser::PBVH_FRAME)+szSegments*sizeof(CBVHParser::BVH_FRAME_SEGMENT));
    if(NULL != pFrame){
        pFrame->dwSegments=szSegments;
        for (NSIZE i=0;i<szSegments;i++){
            for ( int j=0;j<CBVHParser::Channels;j++){
                pFrame->vSegments[i][j]=0.0f;
            }
        }
    }
    return pFrame;
}

CBVHParser::PBVH_FRAME  CBVHParser::ParseFrame          (   NPCBYTE     pbStart,
                                                            NPCBYTE     pbEnd   )   const
{
    ASSERT(NULL != pbStart);
    ASSERT(pbEnd>pbStart);
    vector<BVH_FLOAT>  vFrame;
    NPCBYTE pbWordStart = pbStart,
            pbWordEnd   = NULL;
    do 
    {
        pbWordEnd = ::memory_search(pbWordStart,pbEnd,m_szWordBreak);
        if(         NULL != pbWordEnd
                &&  pbWordEnd<pbEnd    ){
            double df = atof_s((NLPCSTR)pbWordStart,pbWordEnd-pbWordStart);
            vFrame.push_back((BVH_FLOAT)df);
            pbWordStart = pbWordEnd+::strlen(m_szWordBreak);
        }
        else{
            if(::isdigit(pbWordStart[0])){
                double df = atof_s((NLPCSTR)pbWordStart,pbWordEnd-pbWordStart);
                vFrame.push_back((BVH_FLOAT)df);
            }
            break;
        }
    } while (   NULL != pbWordEnd
            &&  pbWordStart<pbEnd   );
    if( !vFrame.empty() ){
        ASSERT(m_vChannels.size()==vFrame.size());
        PBVH_FRAME pFrame = ::NewBVHFrame(m_vJoints.size());
        if(NULL != pFrame){
            NSIZE index =0;
            for (CBVHJoint* pJoint:m_vJoints){
                NSIZE szJointIndex = pJoint->GetJointIndex();
                const vector<ChannelType>& vChannels = pJoint->GetChannels();
                for (ChannelType type:vChannels){
                    pFrame->vSegments[szJointIndex][type]=vFrame[index++];
                }
            }
            return pFrame;
        }
    }
    //
    return NULL;
}

NPCBYTE CBVHParser::SkipLineBreak       (  NPCBYTE   pbBuffer,
                                              unsigned int    cbBuffer    )   const
{
    if(pbBuffer[0]!=m_szLineBreak[0]){
        return pbBuffer;
    }
    size_t len = strlen(m_szLineBreak);
    while (0==::memcmp(pbBuffer,m_szLineBreak,len))
    {
        pbBuffer+=len;
    }
    return pbBuffer;
}

void        CBVHParser::SetDelegate         (   IDelegate   *   pDelegate   )
{
    m_pDelegate=pDelegate;
}

unsigned int            CBVHParser::GetChannelsCount    (   void                                )   const
{
    return m_vChannels.size();
}

unsigned int            CBVHParser::GetJointsCount      (   void                                )   const
{
    return m_vJoints.size();
}

const CBVHParser::CChannel  *       CBVHParser::GetChannel          (   NSIZE       nFrame      )   const
{
    ASSERT( nFrame<m_vChannels.size());
    return m_vChannels.at(nFrame);
}

CBVHParser::PCBVH_FRAME             CBVHParser::GetFrame            (   NSIZE   nFrame      )   const
{
    ASSERT(nFrame<m_vBVHFrame.size());
    return m_vBVHFrame.at(nFrame);
}

const CBVHParser::CBVHJoint    *       CBVHParser::GetRoot     (   void                                )   const
{
    return m_vJoints[0];
}

const CBVHParser::CBVHJoint    *       CBVHParser::GetJoint    (   NSIZE   nIndex      )   const
{
    ASSERT(nIndex<m_vJoints.size());
    return m_vJoints[nIndex];
}

void        CBVHParser::AddChannels         (   CBVHParser::CBVHJoint   *   pJoint      )
{
    for (ChannelType type:pJoint->m_vChannels){
        CChannel* pChannel = new CChannel(type, pJoint);
        m_vChannels.push_back(pChannel);
    }
}